﻿using System;
using System.Runtime.InteropServices;
using System.IO;
using BrawlLib.IO;

namespace System.Audio
{
    public enum WaveFormatTag : ushort
    {
        WAVE_FORMAT_UNKNOWN = 0x0000, /* Microsoft Corporation */
        WAVE_FORMAT_PCM = 0x0001, /* Microsoft Corporation */
        WAVE_FORMAT_ADPCM = 0x0002, /* Microsoft Corporation */
        WAVE_FORMAT_IEEE_FLOAT = 0x0003, /* Microsoft Corporation */
        WAVE_FORMAT_VSELP = 0x0004, /* Compaq Computer Corp. */
        WAVE_FORMAT_IBM_CVSD = 0x0005, /* IBM Corporation */
        WAVE_FORMAT_ALAW = 0x0006, /* Microsoft Corporation */
        WAVE_FORMAT_MULAW = 0x0007, /* Microsoft Corporation */
        WAVE_FORMAT_DTS = 0x0008, /* Microsoft Corporation */
        WAVE_FORMAT_DRM = 0x0009, /* Microsoft Corporation */
        WAVE_FORMAT_WMAVOICE9 = 0x000A, /* Microsoft Corporation */
        WAVE_FORMAT_WMAVOICE10 = 0x000B, /* Microsoft Corporation */
        WAVE_FORMAT_OKI_ADPCM = 0x0010, /* OKI */
        WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel Corporation */
        WAVE_FORMAT_IMA_ADPCM = 0x0011, /*  Intel Corporation */
        WAVE_FORMAT_MEDIASPACE_ADPCM = 0x0012, /* Videologic */
        WAVE_FORMAT_SIERRA_ADPCM = 0x0013, /* Sierra Semiconductor Corp */
        WAVE_FORMAT_DIGISTD = 0x0015, /* DSP Solutions, Inc. */
        WAVE_FORMAT_DIGIFIX = 0x0016, /* DSP Solutions, Inc. */
        WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017, /* Dialogic Corporation */
        WAVE_FORMAT_MEDIAVISION_ADPCM = 0x0018, /* Media Vision, Inc. */
        WAVE_FORMAT_CU_CODEC = 0x0019, /* Hewlett-Packard Company */
        WAVE_FORMAT_YAMAHA_ADPCM = 0x0020, /* Yamaha Corporation of America */
        WAVE_FORMAT_SONARC = 0x0021, /* Speech Compression */
        WAVE_FORMAT_DSPGROUP_TRUESPEECH = 0x0022, /* DSP Group, Inc */
        WAVE_FORMAT_ECHOSC1 = 0x0023, /* Echo Speech Corporation */
        WAVE_FORMAT_AUDIOFILE_AF36 = 0x0024, /* Virtual Music, Inc. */
        WAVE_FORMAT_APTX = 0x0025, /* Audio Processing Technology */
        WAVE_FORMAT_AUDIOFILE_AF10 = 0x0026, /* Virtual Music, Inc. */
        WAVE_FORMAT_PROSODY_1612 = 0x0027, /* Aculab plc */
        WAVE_FORMAT_LRC = 0x0028, /* Merging Technologies S.A. */
        WAVE_FORMAT_DOLBY_AC2 = 0x0030, /* Dolby Laboratories */
        WAVE_FORMAT_GSM610 = 0x0031, /* Microsoft Corporation */
        WAVE_FORMAT_MSNAUDIO = 0x0032, /* Microsoft Corporation */
        WAVE_FORMAT_ANTEX_ADPCME = 0x0033, /* Antex Electronics Corporation */
        WAVE_FORMAT_CONTROL_RES_VQLPC = 0x0034, /* Control Resources Limited */
        WAVE_FORMAT_DIGIREAL = 0x0035, /* DSP Solutions, Inc. */
        WAVE_FORMAT_DIGIADPCM = 0x0036, /* DSP Solutions, Inc. */
        WAVE_FORMAT_CONTROL_RES_CR10 = 0x0037, /* Control Resources Limited */
        WAVE_FORMAT_NMS_VBXADPCM = 0x0038, /* Natural MicroSystems */
        WAVE_FORMAT_CS_IMAADPCM = 0x0039, /* Crystal Semiconductor IMA ADPCM */
        WAVE_FORMAT_ECHOSC3 = 0x003A, /* Echo Speech Corporation */
        WAVE_FORMAT_ROCKWELL_ADPCM = 0x003B, /* Rockwell International */
        WAVE_FORMAT_ROCKWELL_DIGITALK = 0x003C, /* Rockwell International */
        WAVE_FORMAT_XEBEC = 0x003D, /* Xebec Multimedia Solutions Limited */
        WAVE_FORMAT_G721_ADPCM = 0x0040, /* Antex Electronics Corporation */
        WAVE_FORMAT_G728_CELP = 0x0041, /* Antex Electronics Corporation */
        WAVE_FORMAT_MSG723 = 0x0042, /* Microsoft Corporation */
        WAVE_FORMAT_MPEG = 0x0050, /* Microsoft Corporation */
        WAVE_FORMAT_RT24 = 0x0052, /* InSoft, Inc. */
        WAVE_FORMAT_PAC = 0x0053, /* InSoft, Inc. */
        WAVE_FORMAT_MPEGLAYER3 = 0x0055, /* ISO/MPEG Layer3 Format Tag */
        WAVE_FORMAT_LUCENT_G723 = 0x0059, /* Lucent Technologies */
        WAVE_FORMAT_CIRRUS = 0x0060, /* Cirrus Logic */
        WAVE_FORMAT_ESPCM = 0x0061, /* ESS Technology */
        WAVE_FORMAT_VOXWARE = 0x0062, /* Voxware Inc */
        WAVE_FORMAT_CANOPUS_ATRAC = 0x0063, /* Canopus, co., Ltd. */
        WAVE_FORMAT_G726_ADPCM = 0x0064, /* APICOM */
        WAVE_FORMAT_G722_ADPCM = 0x0065, /* APICOM */
        WAVE_FORMAT_DSAT_DISPLAY = 0x0067, /* Microsoft Corporation */
        WAVE_FORMAT_VOXWARE_BYTE_ALIGNED = 0x0069, /* Voxware Inc */
        WAVE_FORMAT_VOXWARE_AC8 = 0x0070, /* Voxware Inc */
        WAVE_FORMAT_VOXWARE_AC10 = 0x0071, /* Voxware Inc */
        WAVE_FORMAT_VOXWARE_AC16 = 0x0072, /* Voxware Inc */
        WAVE_FORMAT_VOXWARE_AC20 = 0x0073, /* Voxware Inc */
        WAVE_FORMAT_VOXWARE_RT24 = 0x0074, /* Voxware Inc */
        WAVE_FORMAT_VOXWARE_RT29 = 0x0075, /* Voxware Inc */
        WAVE_FORMAT_VOXWARE_RT29HW = 0x0076, /* Voxware Inc */
        WAVE_FORMAT_VOXWARE_VR12 = 0x0077, /* Voxware Inc */
        WAVE_FORMAT_VOXWARE_VR18 = 0x0078, /* Voxware Inc */
        WAVE_FORMAT_VOXWARE_TQ40 = 0x0079, /* Voxware Inc */
        WAVE_FORMAT_SOFTSOUND = 0x0080, /* Softsound, Ltd. */
        WAVE_FORMAT_VOXWARE_TQ60 = 0x0081, /* Voxware Inc */
        WAVE_FORMAT_MSRT24 = 0x0082, /* Microsoft Corporation */
        WAVE_FORMAT_G729A = 0x0083, /* AT&T Labs, Inc. */
        WAVE_FORMAT_MVI_MVI2 = 0x0084, /* Motion Pixels */
        WAVE_FORMAT_DF_G726 = 0x0085, /* DataFusion Systems (Pty) (Ltd) */
        WAVE_FORMAT_DF_GSM610 = 0x0086, /* DataFusion Systems (Pty) (Ltd) */
        WAVE_FORMAT_ISIAUDIO = 0x0088, /* Iterated Systems, Inc. */
        WAVE_FORMAT_ONLIVE = 0x0089, /* OnLive! Technologies, Inc. */
        WAVE_FORMAT_SBC24 = 0x0091, /* Siemens Business Communications Sys */
        WAVE_FORMAT_DOLBY_AC3_SPDIF = 0x0092, /* Sonic Foundry */
        WAVE_FORMAT_MEDIASONIC_G723 = 0x0093, /* MediaSonic */
        WAVE_FORMAT_PROSODY_8KBPS = 0x0094, /* Aculab plc */
        WAVE_FORMAT_ZYXEL_ADPCM = 0x0097, /* ZyXEL Communications, Inc. */
        WAVE_FORMAT_PHILIPS_LPCBB = 0x0098, /* Philips Speech Processing */
        WAVE_FORMAT_PACKED = 0x0099, /* Studer Professional Audio AG */
        WAVE_FORMAT_MALDEN_PHONYTALK = 0x00A0, /* Malden Electronics Ltd. */
        WAVE_FORMAT_RHETOREX_ADPCM = 0x0100, /* Rhetorex Inc. */
        WAVE_FORMAT_IRAT = 0x0101, /* BeCubed Software Inc. */
        WAVE_FORMAT_VIVO_G723 = 0x0111, /* Vivo Software */
        WAVE_FORMAT_VIVO_SIREN = 0x0112, /* Vivo Software */
        WAVE_FORMAT_DIGITAL_G723 = 0x0123, /* Digital Equipment Corporation */
        WAVE_FORMAT_SANYO_LD_ADPCM = 0x0125, /* Sanyo Electric Co., Ltd. */
        WAVE_FORMAT_SIPROLAB_ACEPLNET = 0x0130, /* Sipro Lab Telecom Inc. */
        WAVE_FORMAT_SIPROLAB_ACELP4800 = 0x0131, /* Sipro Lab Telecom Inc. */
        WAVE_FORMAT_SIPROLAB_ACELP8V3 = 0x0132, /* Sipro Lab Telecom Inc. */
        WAVE_FORMAT_SIPROLAB_G729 = 0x0133, /* Sipro Lab Telecom Inc. */
        WAVE_FORMAT_SIPROLAB_G729A = 0x0134, /* Sipro Lab Telecom Inc. */
        WAVE_FORMAT_SIPROLAB_KELVIN = 0x0135, /* Sipro Lab Telecom Inc. */
        WAVE_FORMAT_G726ADPCM = 0x0140, /* Dictaphone Corporation */
        WAVE_FORMAT_QUALCOMM_PUREVOICE = 0x0150, /* Qualcomm, Inc. */
        WAVE_FORMAT_QUALCOMM_HALFRATE = 0x0151, /* Qualcomm, Inc. */
        WAVE_FORMAT_TUBGSM = 0x0155, /* Ring Zero Systems, Inc. */
        WAVE_FORMAT_MSAUDIO1 = 0x0160, /* Microsoft Corporation */
        WAVE_FORMAT_WMAUDIO2 = 0x0161, /* Microsoft Corporation */
        WAVE_FORMAT_WMAUDIO3 = 0x0162, /* Microsoft Corporation */
        WAVE_FORMAT_WMAUDIO_LOSSLESS = 0x0163, /* Microsoft Corporation */
        WAVE_FORMAT_WMASPDIF = 0x0164, /* Microsoft Corporation */
        WAVE_FORMAT_UNISYS_NAP_ADPCM = 0x0170, /* Unisys Corp. */
        WAVE_FORMAT_UNISYS_NAP_ULAW = 0x0171, /* Unisys Corp. */
        WAVE_FORMAT_UNISYS_NAP_ALAW = 0x0172, /* Unisys Corp. */
        WAVE_FORMAT_UNISYS_NAP_16K = 0x0173, /* Unisys Corp. */
        WAVE_FORMAT_CREATIVE_ADPCM = 0x0200, /* Creative Labs, Inc */
        WAVE_FORMAT_CREATIVE_FASTSPEECH8 = 0x0202, /* Creative Labs, Inc */
        WAVE_FORMAT_CREATIVE_FASTSPEECH10 = 0x0203, /* Creative Labs, Inc */
        WAVE_FORMAT_UHER_ADPCM = 0x0210, /* UHER informatic GmbH */
        WAVE_FORMAT_QUARTERDECK = 0x0220, /* Quarterdeck Corporation */
        WAVE_FORMAT_ILINK_VC = 0x0230, /* I-link Worldwide */
        WAVE_FORMAT_RAW_SPORT = 0x0240, /* Aureal Semiconductor */
        WAVE_FORMAT_ESST_AC3 = 0x0241, /* ESS Technology, Inc. */
        WAVE_FORMAT_GENERIC_PASSTHRU = 0x0249,
        WAVE_FORMAT_IPI_HSX = 0x0250, /* Interactive Products, Inc. */
        WAVE_FORMAT_IPI_RPELP = 0x0251, /* Interactive Products, Inc. */
        WAVE_FORMAT_CS2 = 0x0260, /* Consistent Software */
        WAVE_FORMAT_SONY_SCX = 0x0270, /* Sony Corp. */
        WAVE_FORMAT_FM_TOWNS_SND = 0x0300, /* Fujitsu Corp. */
        WAVE_FORMAT_BTV_DIGITAL = 0x0400, /* Brooktree Corporation */
        WAVE_FORMAT_QDESIGN_MUSIC = 0x0450, /* QDesign Corporation */
        WAVE_FORMAT_VME_VMPCM = 0x0680, /* AT&T Labs, Inc. */
        WAVE_FORMAT_TPC = 0x0681, /* AT&T Labs, Inc. */
        WAVE_FORMAT_OLIGSM = 0x1000, /* Ing C. Olivetti & C., S.p.A. */
        WAVE_FORMAT_OLIADPCM = 0x1001, /* Ing C. Olivetti & C., S.p.A. */
        WAVE_FORMAT_OLICELP = 0x1002, /* Ing C. Olivetti & C., S.p.A. */
        WAVE_FORMAT_OLISBC = 0x1003, /* Ing C. Olivetti & C., S.p.A. */
        WAVE_FORMAT_OLIOPR = 0x1004, /* Ing C. Olivetti & C., S.p.A. */
        WAVE_FORMAT_LH_CODEC = 0x1100, /* Lernout & Hauspie */
        WAVE_FORMAT_NORRIS = 0x1400, /* Norris Communications, Inc. */
        WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS = 0x1500, /* AT&T Labs, Inc. */
        WAVE_FORMAT_MPEG_ADTS_AAC = 0x1600, /* Microsoft Corporation */
        WAVE_FORMAT_MPEG_RAW_AAC = 0x1601, /* Microsoft Corporation */
        WAVE_FORMAT_MPEG_LOAS = 0x1602, /* Microsoft Corporation (MPEG-4 Audio Transport Streams (LOAS/LATM) */
        WAVE_FORMAT_NOKIA_MPEG_ADTS_AAC = 0x1608, /* Microsoft Corporation */
        WAVE_FORMAT_NOKIA_MPEG_RAW_AAC = 0x1609, /* Microsoft Corporation */
        WAVE_FORMAT_VODAFONE_MPEG_ADTS_AAC = 0x160A, /* Microsoft Corporation */
        WAVE_FORMAT_VODAFONE_MPEG_RAW_AAC = 0x160B, /* Microsoft Corporation */
        WAVE_FORMAT_MPEG_HEAAC = 0x1610, /* Microsoft Corporation (MPEG-2 AAC or MPEG-4 HE-AAC v1/v2 streams with any payload (ADTS, ADIF, LOAS/LATM, RAW). Format block includes MP4 AudioSpecificConfig() -- see HEAACWAVEFORMAT below */
        WAVE_FORMAT_DVM = 0x2000, /* FAST Multimedia AG */
        WAVE_FORMAT_EXTENSIBLE = 0xFFFE /* Microsoft */
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public unsafe struct WaveFormatEx
    {
        public WaveFormatTag wFormatTag;
        public ushort nChannels;
        public uint nSamplesPerSec;
        public uint nAvgBytesPerSec;
        public ushort nBlockAlign;
        public ushort wBitsPerSample;
        public ushort cbSize;

        public WaveFormatEx(WaveFormatTag format, int channels, int frequency, int bitsPerSample)
        {
            wFormatTag = format;
            nChannels = (ushort)channels;
            nSamplesPerSec = (uint)frequency;
            nBlockAlign = (ushort)(bitsPerSample * channels / 8);
            nAvgBytesPerSec = nBlockAlign * nSamplesPerSec;
            wBitsPerSample = (ushort)bitsPerSample;
            cbSize = 0;
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct WaveFormatExtensible
    {
        public WaveFormatEx Format;
        public ushort wValidBitsPerSample;
        public ushort wSamplesPerBlock;
        public ushort wReserved;
        public uint dwChannelMask;
        public uint SubFormat;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    unsafe struct RIFFHeader
    {
        public const uint RIFFTag = 0x46464952;
        public const uint WAVETag = 0x45564157;
        public const uint LISTTag = 0x5453494C;

        public uint _tag;
        public uint _length;
        public uint _waveTag;
        public fmtChunk _fmtChunk;
        public dataChunk _listChunk { get { return *(dataChunk*)(Address + 12 + _fmtChunk.GetSize()); } }
        public dataChunk _dataChunk 
        {
            get { return *(dataChunk*)(Address + 12 + _fmtChunk.GetSize() + (_listChunk._chunkTag == LISTTag ? 8 + _listChunk._chunkSize : 0)); }
            set { *(dataChunk*)(Address + 12 + _fmtChunk.GetSize()) = value; }
        }

        public RIFFHeader(int format, int channels, int bitsPerSample, int sampleRate, int numSamples)
        {
            _tag = RIFFTag;
            _waveTag = WAVETag;
            _fmtChunk = new fmtChunk(format, channels, bitsPerSample, sampleRate);
            uint dataLen = (uint)(numSamples * _fmtChunk._blockAlign);
            _length = 0;
            _dataChunk = new dataChunk(dataLen);
            _length = (dataLen + GetSize()) - 8;
        }

        public uint GetSize() { return (uint)(12 + _fmtChunk.GetSize() + 8 + (_listChunk._chunkTag == LISTTag ? 8 + _listChunk._chunkSize : 0)); }
        internal byte* Address { get { fixed (void* ptr = &this)return (byte*)ptr; } }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    unsafe struct fmtChunk
    {
        public const uint fmtTag = 0x20746D66;

        public uint _chunkTag;
        public uint _chunkSize;
        public ushort _format;
        public ushort _channels;
        public uint _samplesSec;
        public uint _avgBytesSec;
        public ushort _blockAlign;
        public ushort _bitsPerSample;
        public ushort _extraParamSize;

        //For some reason, if these aren't here, dataChunk bytes are 0
        public ushort _randomFiller1;
        public uint _randomFiller2;

        public int GetSize() { return (int)(8 + _chunkSize); }
        public fmtChunk(int format, int channels, int bitsPerSample, int sampleRate)
        {
            _chunkTag = fmtTag;
            _chunkSize = 0x10;
            _format = (ushort)format;
            _channels = (ushort)channels;
            _samplesSec = (uint)sampleRate;
            _blockAlign = (ushort)(bitsPerSample / 8 * channels);
            _avgBytesSec = (uint)(sampleRate * _blockAlign);
            _bitsPerSample = (ushort)bitsPerSample;
            _extraParamSize = 
                _randomFiller1 = 
                0;
            _randomFiller2 = 0;
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    unsafe struct dataChunk
    {
        public const uint dataTag = 0x61746164;
        public const int Size = 8;

        public uint _chunkTag;
        public uint _chunkSize;

        public dataChunk(uint dataLength)
        {
            _chunkTag = dataTag;
            _chunkSize = dataLength;
        }

        internal byte* Address { get { fixed (void* ptr = &this)return (byte*)ptr; } }
    }

    public unsafe static class WAV
    {
        public static IAudioStream FromFile(string path)
        {
            return new PCMStream(FileMap.FromFile(path, FileMapProtect.Read));
        }

        public static void ToFile(IAudioStream source, string path)
        {
            using (FileStream stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, 8, FileOptions.SequentialScan))
            {
                //Estimate size
                int outLen = 44 + (source.Samples * source.Channels * 2);

                //Create file map
                stream.SetLength(outLen);
                using (FileMap map = FileMap.FromStreamInternal(stream, FileMapProtect.ReadWrite, 0, outLen))
                {
                    RIFFHeader* riff = (RIFFHeader*)map.Address;
                    *riff = new RIFFHeader(1, source.Channels, 16, source.Frequency, source.Samples);

                    source.SamplePosition = 0;
                    source.ReadSamples(map.Address + 44, source.Samples);
                }
            }
        }
    }
}
